/**
 * <p>Title: Game.java</p>
 * <p>Description: CS 343 Final Project. This class is the core of the game itself</p>
 * @author Tim Olmstead, Brady Fetting, Erik Brown, Thales Kirk
 * @email timothy.olmstead@my.uwrf.edu, brady.fetting@my.uwrf.edu, 
 * 			erik.brown@my.uwrf.edu, thales.kirk@my.uwrf.edu
 * @date November 15, 2013
 */

package GameState;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.util.ConcurrentModificationException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextField;

import Main.Begin;
import Obstacles.Obstacle;
import Obstacles.ObstacleContainer;
import Obstacles.ObstacleSpawner;
import Settings.Config;
import Charactors.Hero;
import Charactors.Mob;
import Charactors.MobContainer;
import Charactors.MobSpawner;
import Components.MoveableBackground;
import Components.Input;
import Components.Music;
import Components.Physics;
import Components.Record;
import Components.Scoring;
import Components.SoundEffects;

public class Game extends JPanel implements ActionListener {
	private SoundEffects soundFX = new SoundEffects();
	private ExecutorService pool = Executors.newFixedThreadPool(4);
	private JButton restart;
	private JButton mainMenu;
	private JButton leaderboard;
	private JButton submitScore;
	private JButton AudioButton;
	private JTextField usernameField;
	private String username = "";
	private boolean muted = false;
	private boolean gameJustStarted;
	private boolean gameEnded = false;
	private static boolean paused = false;
	// public access
	public static Hero player;
	private static int groundLevel;
	public static final double gavity = 0.4;
	public static final double terminalVelocity = 6
			;
	public static final String scoreStr = "SCORE: ";
	public static final String mphStr = "MPH: ";

	
	private static MoveableBackground background;
	private static int gameSpeed;
	private String directory = "src/images/";

	private Image panel = new ImageIcon(directory + "panel.png").getImage();
	private Image panelBig = new ImageIcon(directory + "panelBig.png")
			.getImage();
	private Image loadFilter = new ImageIcon(directory + "filter.png")
			.getImage();
	private Image pauseImage = new ImageIcon(directory + "paused.png")
			.getImage();

	
	private int PLAYER_X_LOC = 50;

	private Input input;

	private static final long serialVersionUID = 1L;
	private static Music backgroundMusic = new Music();

	// Starts component threads
	private static Physics phys;
	private static Scoring score;
	private static ObstacleSpawner obsSpawn;
	private static ObstacleContainer obstacleContainer;
	private static MobSpawner mobSpawn;
	private static MobContainer mobcontainer;

	/**
	 * 
	 * @param frameWidth
	 * @param frameHeight
	 * @description this sets the window for the game, fills it will essential
	 *              images, and begins threads crucial to the game
	 */
	@SuppressWarnings("static-access")
	public Game(int frameWidth, int frameHeight) {
		if (backgroundMusic.isMuted())
			muted = !muted;
		setPhys(new Physics());
		score = new Scoring();
		obsSpawn = new ObstacleSpawner();
		obstacleContainer = new ObstacleContainer();
		mobSpawn = new MobSpawner();
		mobcontainer = new MobContainer();

		getPhys().enableThread();
		score.enableThread();
		obsSpawn.enableThread();
		mobSpawn.enableThread();
		setLayout(null);

		// Starts the background music to run as a separate thread
		if (!backgroundMusic.isMuted()) {
			try {
				backgroundMusic.gamePLayMusic();
			} catch (IOException e1) {
				e1.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
		if (!backgroundMusic.isMuted()) {
			AudioButton = new JButton(new ImageIcon("src/images/Sound.png"));
			AudioButton.setRolloverIcon(new ImageIcon(
					"src/images/soundHover.png"));
		} else {
			AudioButton = new JButton(new ImageIcon("src/images/mute.png"));
			AudioButton.setRolloverIcon(new ImageIcon(
					"src/images/muteHover.png"));
		}
		AudioButton.setBounds(Config.FRAME_WIDTH - 60, 10, 50, 50);
		// removes background of button
		AudioButton.setContentAreaFilled(false);
		// removes button border
		AudioButton.setBorder(BorderFactory.createEmptyBorder());
		AudioButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if (muted) {
					AudioButton.setIcon(new ImageIcon("src/images/sound.png"));
					AudioButton.setRolloverIcon(new ImageIcon(
							"src/images/soundHover.png"));
					try {
						backgroundMusic.gamePLayMusic();
					} catch (IOException e1) {
						e1.printStackTrace();
					} catch (Exception e1) {
						e1.printStackTrace();
					}
				} else {
					AudioButton.setIcon(new ImageIcon("src/images/mute.png"));
					AudioButton.setRolloverIcon(new ImageIcon(
							"src/images/muteHover.png"));
					backgroundMusic.stopDaBeats();
				}
				muted = !muted;
				backgroundMusic.switchMuted();

				// After button press, request focus back to the JPanel so key
				// input works
				requestFocusInWindow();
			}
		});
		add(AudioButton);

		background = new MoveableBackground();
		gameSpeed = 3;

		groundLevel = 414;
		player = new Hero(PLAYER_X_LOC, groundLevel);

		input = new Input();
		this.addKeyListener(input.getKeys());
		setFocusable(true);

		// Starting physics
		pool.submit(getPhys());
		// physicsThread.start();

		// used to start scoring right when all things are done loaded
		gameJustStarted = true;

		(new javax.swing.Timer(16, this)).start(); // call actionPerformed every
													// 16 milliseconds for
													// graphics
	}

	/**
	 * @param g
	 * @description this is incharge of starting the core aspects of the game
	 *              such as scoring and obstacle spawning threads
	 */
	@SuppressWarnings("static-access")
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2d = (Graphics2D) g;

		if (gameJustStarted) {
			// StartingScoringThread
			pool.submit(obsSpawn);
			pool.submit(score);
			pool.submit(mobSpawn);
			gameJustStarted = false;
		}

		// antialiasing on! Woot!
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);

		// blue back
		g2d.drawImage(background.getBlue(), 0, 0, null);

		g2d.drawImage(background.getBackClouds1(),
				background.getX1backClouds(), background.getY1backClouds(),
				null);
		g2d.drawImage(background.getBackClouds2(),
				background.getX2backClouds(), background.getY2backClouds(),
				null);

		g2d.drawImage(background.getForeClouds1(),
				background.getX1foreClouds(), background.getY1foreClouds(),
				null);
		g2d.drawImage(background.getForeClouds2(),
				background.getX2foreClouds(), background.getY2backClouds(),
				null);

		// background
		if (background.getX1() < Config.FRAME_WIDTH)
			g2d.drawImage(background.getBackgroundImage1(), background.getX1(),
					background.getY1(), null);
		if (background.getX2() < Config.FRAME_WIDTH)
			g2d.drawImage(background.getBackgroundImage2(), background.getX2(),
					background.getY2(), null);

		// scoring/mph

		if (!gameEnded) {
			g2d.setFont(new Font("SansSerif", Font.BOLD, 25));
			g2d.drawImage(panel, 0, 0, null);
			g2d.setColor(Color.WHITE);
			g2d.drawString(scoreStr + score.getIncrementScore(), 16, 74);
			g2d.drawString(mphStr + player.getSpeed().getSpeed(), 16, 108);
			g2d.setColor(Color.BLACK);
			g2d.drawString(scoreStr + score.getIncrementScore(), 18, 75);
			g2d.drawString(mphStr + player.getSpeed().getSpeed(), 18, 110);
		}
		// Draw Obstacles
		try {
			for (Obstacle x : getObstacleContainer().getObs()) {
				g2d.drawImage(x.getImage(), x.getXloc(), x.getYloc(), null);
				// g2d.drawPolygon(x.getPolygon());
			}
		} catch (ConcurrentModificationException a) {
			System.err.println("Graphics problem with painting obstacles");
		}
		try {
			for (Mob x : getMobcontainer().getMobs()) {
				g2d.drawImage(x.getImage(), x.getxLoc(), x.getyLoc(), null);
			}
		} catch (ConcurrentModificationException a) {
			System.err.println("Graphics problem with painting mob");
		}

		// Character
		g2d.drawImage(player.getImage(), player.getxLoc(), player.getyLoc(),
				null);
		// g2d.drawPolygon(player.getPolygon());

		if (paused) {
			g2d.drawImage(loadFilter, 0, 0, loadFilter.getWidth(null),
					loadFilter.getHeight(null), null);
			g2d.drawImage(pauseImage,
					(Config.FRAME_WIDTH - pauseImage.getWidth(null)) / 2,
					(Config.FRAME_HEIGHT - pauseImage.getHeight(null)) / 2,
					null);
		}

		if (gameEnded) {
			g2d.drawImage(loadFilter, 0, 0, loadFilter.getWidth(null),
					loadFilter.getHeight(null), null);
			g2d.setFont(new Font("SansSerif", Font.BOLD, 30));
			g2d.drawImage(panelBig, 0, 0, null);
			g2d.setColor(Color.WHITE);
			g2d.drawString("USERNAME : " + username, 150, 180);
			g2d.drawString("FINAL " + scoreStr + score.getFinalScore(), 150,
					220);
			g2d.drawString("TOP " + mphStr + player.getSpeed().getTopSpeed(),
					150, 260);
			g2d.setColor(Color.BLACK);
			g2d.drawString("USERNAME : " + username, 152, 182);
			g2d.drawString("FINAL " + scoreStr + score.getFinalScore(), 152,
					222);
			g2d.drawString("TOP " + mphStr + player.getSpeed().getTopSpeed(),
					152, 262);
		}

		if (!player.isAlive()) {

			endGame();
		}

	}

	// function for when the player collides with object
	/**
	 * @param none
	 * @description when called, the scoring will stop, the player stops, and
	 *              game speed stops, and the player die animation is called
	 */
	@SuppressWarnings("static-access")
	public void endGame() {
		if (gameEnded) {
			return;
		}
		gameEnded = true;

		// stop threads
		getPhys().endThread();
		score.endThead();
		obsSpawn.endThread();
		mobSpawn.endThread();

		this.removeKeyListener(input.getKeys());
		player.stopX();
		gameSpeed = 0;
		if (!backgroundMusic.isMuted())
			backgroundMusic.stopDaBeats();

		restart = new JButton(new ImageIcon("src/images/restartButton.png"));
		restart.setRolloverIcon(new ImageIcon(
				"src/images/restartButtonHover.png"));
		restart.setBounds(190, 45, 175, 75);
		// removes background of button
		restart.setContentAreaFilled(false);
		// removes button border
		restart.setBorder(BorderFactory.createEmptyBorder());
		restart.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// play the game
				getObstacleContainer().clearContainer();
				getMobcontainer().clearContainer();
				Begin.app.setGameState(1);
			}
		});
		restart.addMouseListener(new MouseListener() {
			public void mouseClicked(MouseEvent arg0) {
			}

			public void mouseEntered(MouseEvent arg0) {
				soundFX.playTickSound();
			}

			public void mouseExited(MouseEvent arg0) {
			}

			public void mousePressed(MouseEvent arg0) {
			}

			public void mouseReleased(MouseEvent arg0) {
			}

		});
		restart.setVisible(true);
		add(restart);

		mainMenu = new JButton(new ImageIcon("src/images/MainMenuButton.png"));
		mainMenu.setRolloverIcon(new ImageIcon(
				"src/images/MainMenuButtonHover.png"));
		mainMenu.setBounds(350, 45, 175, 75);
		// removes background of button
		mainMenu.setContentAreaFilled(false);
		// removes button border
		mainMenu.setBorder(BorderFactory.createEmptyBorder());
		mainMenu.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// play the game
				getObstacleContainer().clearContainer();
				getMobcontainer().clearContainer();
				Begin.app.setGameState(0);
			}
		});
		mainMenu.addMouseListener(new MouseListener() {
			public void mouseClicked(MouseEvent arg0) {
			}

			public void mouseEntered(MouseEvent arg0) {
				soundFX.playTickSound();
			}

			public void mouseExited(MouseEvent arg0) {
			}

			public void mousePressed(MouseEvent arg0) {
			}

			public void mouseReleased(MouseEvent arg0) {
			}

		});
		mainMenu.setVisible(true);
		add(mainMenu);

		leaderboard = new JButton(new ImageIcon(
				"src/images/leaderboardbutton.png"));
		leaderboard.setRolloverIcon(new ImageIcon(
				"src/images/leaderboardbuttonHover.png"));
		leaderboard.setBounds(510, 45, 175, 75);
		// removes background of button
		leaderboard.setContentAreaFilled(false);
		// removes button border
		leaderboard.setBorder(BorderFactory.createEmptyBorder());
		leaderboard.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// go to the leader board but clear container first
				getObstacleContainer().clearContainer();
				getMobcontainer().clearContainer();
				Begin.app.setGameState(2);
			}
		});
		leaderboard.addMouseListener(new MouseListener() {
			public void mouseClicked(MouseEvent arg0) {
			}

			public void mouseEntered(MouseEvent arg0) {
				soundFX.playTickSound();
			}

			public void mouseExited(MouseEvent arg0) {
			}

			public void mousePressed(MouseEvent arg0) {
			}

			public void mouseReleased(MouseEvent arg0) {
			}

		});
		leaderboard.setVisible(true);
		add(leaderboard);

		submitScore = new JButton(new ImageIcon("src/images/submit.png"));
		submitScore.setBounds(400, 290, 100, 50);
		// removes background of button
		submitScore.setContentAreaFilled(false);
		// removes button border
		submitScore.setBorder(BorderFactory.createEmptyBorder());
		submitScore.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				username = usernameField.getText();
				remove(submitScore);
				remove(usernameField);
				Begin.app.getDatabase().setRecord(
						new Record(username, (int) score.getFinalScore(),
								player.getSpeed().getTopSpeed()));
			}
		});
		submitScore.addMouseListener(new MouseListener() {
			public void mouseClicked(MouseEvent arg0) {
			}

			public void mouseEntered(MouseEvent arg0) {
				soundFX.playTickSound();
			}

			public void mouseExited(MouseEvent arg0) {
			}

			public void mousePressed(MouseEvent arg0) {
			}

			public void mouseReleased(MouseEvent arg0) {
			}

		});
		submitScore.setVisible(true);
		add(submitScore);
		// ends threads
		usernameField = new JTextField();
		Font font1 = new Font("SansSerif", Font.BOLD, 30);
		usernameField.setOpaque(false);
		usernameField.setFont(font1);
		usernameField.setForeground(Color.BLACK);
		usernameField.setBounds(355, 152, 200, 40);
		add(usernameField);

		pool.shutdown();

		// sets score back to 0
		score.resetScore();
	}

	/**
	 * param arg0
	 * 
	 * @description when called it will refresh and repaint the drawings
	 */
	public void actionPerformed(ActionEvent arg0) {
		// player.move();
		repaint(); // refresh all of the drawing

	}

	/**
	 * @param none
	 * @description gets and returns the value of the ground level
	 */
	public synchronized static int getGroundLevel() {
		return groundLevel;
	}

	/**
	 * @param groundLevel
	 * @description sets the value of the ground level
	 */
	public synchronized static void setGroundLevel(int groundLevel) {
		Game.groundLevel = groundLevel;
	}

	/**
	 * @param none
	 * @description gets the value of the game speed
	 */
	public synchronized static int getGameSpeed() {
		return gameSpeed;
	}

	/**
	 * @param gameSpeed
	 * @description sets the value of the game speed
	 */
	public synchronized static void setGameSpeed(int gameSpeed) {
		Game.gameSpeed = gameSpeed;
	}

	/**
	 * @param none
	 * @description returns the obstacleContainer
	 */
	public static synchronized ObstacleContainer getObstacleContainer() {
		return obstacleContainer;
	}

	/**
	 * @param obstacleContainer
	 * @description sets the obstableContainer
	 */
	public static synchronized void setObstacleContainer(
			ObstacleContainer obstacleContainerNew) {
		obstacleContainer = obstacleContainerNew;
	}

	/**
	 * @param none
	 * @description gets the movableBackground for the game
	 */
	public synchronized static MoveableBackground getMoveableBackground() {
		return background;
	}

	/**
	 * @param Mbackground
	 * @description sets the movable background for the game
	 */
	public synchronized static void setMoveableBackground(
			MoveableBackground Mbackground) {
		background = Mbackground;
	}

	/**
	 * @param none
	 * @description pauses and unpauses the game if the user wishes to
	 *              pause/unpause
	 */
	public static void pauseUnpause() {
		if (!paused) {
			getPhys().suspendThread();
			score.suspendThread();
			obsSpawn.suspendThread();
			mobSpawn.suspendThread();
		} else {
			getPhys().unsuspendThread();
			score.unsuspendThread();
			obsSpawn.unsuspendThread();
			mobSpawn.unsuspendThread();
		}
		paused = !paused;
	}

	/**
	 * @param none
	 * @description gets the spawning of the mob
	 */
	public static MobSpawner getMobSpawn() {
		return mobSpawn;
	}

	/**
	 * @param mobSpawn
	 * @description sets the mob spawner within the game to spawn the sprites
	 */
	public static void setMobSpawn(MobSpawner mobSpawn) {
		Game.mobSpawn = mobSpawn;
	}

	/**
	 * @param none
	 * @description gets the mob container for the game
	 */
	public static MobContainer getMobcontainer() {
		return mobcontainer;
	}

	/**
	 * @param mobcontainer
	 * @description sets the mob container for the game
	 */
	public static void setMobcontainer(MobContainer mobcontainer) {
		Game.mobcontainer = mobcontainer;
	}

	public static Physics getPhys() {
		return phys;
	}

	public static void setPhys(Physics phys) {
		Game.phys = phys;
	}

}
